package com.imist.nettybasic2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * 1.我们首先学习了 Netty 服务端启动的流程，创建一个引导类，然后给他指定线程模型，IO模型，连接读写处理逻辑，绑定端口之后，服务端就启动起来了。
 * 2.然后，我们学习到 bind 方法是异步的，我们可以通过这个异步机制来实现端口递增绑定。
 * 3.最后呢，我们讨论了 Netty 服务端启动额外的参数，主要包括给服务端 Channel 或者客户端 Channel 设置属性值，设置底层 TCP 参数。
 */
public class NettyServer {
    public static void main(String[] args) {
        //1. 创建一个引导类
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //主要负责创建新连接,表示监听端口，accept 新连接的线程组
        NioEventLoopGroup boss = new NioEventLoopGroup();
        //主要用于读取数据以及业务逻辑处理
        NioEventLoopGroup work = new NioEventLoopGroup();
        // 2. 为引导类指定线程模型和IO模型，
        serverBootstrap.group(boss,work)
                .channel(NioServerSocketChannel.class)// BIO : OioServerSocketChannel.class
                //channel属性，作用于服务端的channel

                //给服务端的 channel，也就是NioServerSocketChannel指定一些自定义属性，然后我们可以通过channel.attr()取出这个属性
                .attr(AttributeKey.newInstance("serverName"),"serverName")
                //表示系统用于临时存放已完成三次握手的请求的队列的最大长度，如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
                .option(ChannelOption.SO_BACKLOG,1024)

                //childOption连接属性 可以作用于每条连接

                //childAttr可以给每一条连接指定自定义属性，然后后续我们可以通过channel.attr()取出该属性。
                .childAttr(AttributeKey.newInstance("clientKey"),"clientValue")
                //是否开启TCP底层心跳机制，true表示开启
                .childOption(ChannelOption.SO_KEEPALIVE,true)
                // 表示是否开启Nagle算法，true表示关闭，false表示开启.要求消息的实时性就关闭，要求减少发送的次数减少网络交互就开启
                .childOption(ChannelOption.TCP_NODELAY,true)
                //3. 编写读写处理逻辑
                .childHandler(new ChannelInitializer<NioSocketChannel>() {//这里注意是SocketChannel
                    @Override
                    protected void initChannel(NioSocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new StringDecoder());
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
                                    System.out.println(msg);
                                }
                            });
                    }
                });
                //4. 绑定端口,此方法为一个异步方法，调用之后直接返回ChannelFuture，通过添加监听器监听绑定的结果
                ChannelFuture future = serverBootstrap.bind(8000)
                        .addListener(new GenericFutureListener<Future<? super Void>>() {
                            public void operationComplete(Future<? super Void> future) throws Exception {
                                    if (future.isSuccess()){
                                        System.out.println("端口绑定成功！");
                                    }else {
                                        System.out.println("端口绑定失败！");
                                    }
                            }
                });
    }

}
